package model.management;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import model.entity.Item;

import common.Barcode;
import common.Result;

/**
 * Tracks the instance of the items in the database. Also provides functionality for running
 * reports on items.
 * 
 * @author Ian Ryu, Ryan Cheatham
 * 
 *         {@invariant items.size >= 0}
 */

public enum ItemManager
{
  INSTANCE;

  private List<Item> items                = new ArrayList<Item>();

  private int        currentItemBarcodeID = 1;

  /**
   * Gets a list of all items that are expired.
   * 
   * @return a List of expired items.
   * 
   *         {@pre items != null}
   */
  public List<Item> getExpiredItems()
  {
    List<Item> expiredItems = new ArrayList<Item>();

    for (Item item : items)
    {
      if (item.getExpirationDate().compareTo(new Date()) < 0)
      {
        expiredItems.add(item);
      }
    }
    return expiredItems;
  }

  /**
   * Validates whether items are removed or not.
   * 
   * @param since
   *          The date user want to report from. (type Date).
   * 
   * @return a List of expired items.
   * 
   *         {@pre since != null}
   */
  public List<Item> getRemovedItems(Date since)
  {
    List<Item> removedItems = new ArrayList<Item>();

    for (Item item : items)
    {
      if (item.getExitTime().compareTo(since) < 0)
      {
        removedItems.add(item);
      }
    }
    return removedItems;
  }

  /**
   * Validates whether the given item can be added to item.
   * 
   * @param item
   *          The item to validate.
   * @return a result, whose status indicates whether the given item can be added to this
   *         container.
   * 
   *         {@pre item != null}
   * 
   */
  public static Result validateAddItem(Item item)
  {
    if (item == null)
    {
      return new Result(false, "Error: Null Item");
    }

    Result validItem = item.validateItem();
    if (!validItem.getStatus())
    {
      return validItem;
    }
    if (INSTANCE.items.contains(item))
    {
      return new Result(false, "Error: item is already in container.");
    }
    return new Result(true);
  }

  /**
   * Validates whether the given item can be added to this item container. If so, the item is added
   * to this container.
   * 
   * @param item
   *          The item to be added to this container.
   * 
   * @return a result, indicating whether this operation was successful or not. {@pre item != null}
   *         {@post items.size == (old)items.size() + 1}
   * 
   */
  public static Result addItem(Item item)
  {
    Result validAdd = validateAddItem(item);
    if (!validAdd.getStatus())
    {
      return validAdd;
    }
    INSTANCE.items.add(item);
    return new Result(true);
  }

  /**
   * Validates whether the given item can be removed from items.
   * 
   * @param item
   *          The item to validate.
   * @return a result, whose status indicates whether the given item can be removed from this
   *         container.
   * 
   *         {@pre item != null}
   * 
   */
  public static Result validateRemoveItem(Item item)
  {
    if (item == null)
    {
      return new Result(false, "Error: item is Null");
    }
    if (!INSTANCE.items.contains(item))
    {
      return new Result(false, "Error: item is not in container.");
    }
    return new Result(true);
  }

  /**
   * Validates whether the given item can be removed from this item container. If so, the item is
   * removed from this container.
   * 
   * @param item
   *          The item to be removed from this container.
   * 
   * @return a result, indicating whether this operation was successful or not.
   * 
   *         {@pre item != null} {@post items.size == (old)items.size() - 1}
   */
  public static Result removeItem(Item item)
  {
    Result validRemove = validateRemoveItem(item);
    if (!validRemove.getStatus())
    {
      return validRemove;
    }
    INSTANCE.items.remove(item);
    return new Result(true);
  }

  public static Result validUpdateItem(Item oldItem, Item newItem)
  {
    if (oldItem == null || newItem == null)
    {
      return new Result(false, "Error: Null item");
    }
    Result validItem = newItem.validateItem();
    if (!validItem.getStatus())
    {
      return validItem;
    }
    if (!INSTANCE.items.contains(oldItem))
    {
      return new Result(false, "Error: item is not in container");
    }
    return new Result(true);
  }

  public static Result updateItem(Item oldItem, Item newItem)
  {
    Result validUpdate = validUpdateItem(oldItem, newItem);
    if (!validUpdate.getStatus())
    {
      return validUpdate;
    }

    int index = INSTANCE.items.indexOf(oldItem);
    INSTANCE.items.set(index, newItem);
    return new Result(true);
  }

  /**
   * @return An iterator of all <code>Product</code>s in the system.
   */
  public static Iterator<Item> getItems()
  {
    return Collections.unmodifiableList(INSTANCE.items).iterator();
  }

  /**
   * Generates and returns a new {@link Barcode} to be used to identify a unique item in the
   * database.
   * 
   * {@post retval is a valid, unique internal barcode}
   */
  public String generateNewItemBarcode()
  {
    return String.format("04%010d", currentItemBarcodeID++);
  }

  public static void clear()
  {
    INSTANCE.items.clear();
  }
}
